#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "3d7p_aceMesh.h"
#include "task.h"
__thread_local volatile unsigned long get_reply,put_reply;
__thread_local_fix double A_slave_data[BLKX+2][BLKY+2][BLKZ+2];
__thread_local_fix double A_slave_comp[BLKX][BLKY][BLKZ];
//Record the logical information of this time executed task
__thread_local_fix int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
extern double A[2][SIZEX][SIZEY][SIZEZ];

#define alpha_d 0.0876
#define beta_d  0.0765

void stencil_core(targs* args) 
{ 
//0. assumptions       
//data region: [starti-1,endi] [startj-1,endj] [startk-1,endk]  
//compute region: [starti, endi) [startj,endj) [startk,endk)
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   s_startk = args->startk;
   s_endk = args->endk;
//1 fetch data
  get_reply=0;  
#ifdef __STRIDE_DMA
   if((SIZEZ-2)%BLKZ != 0 && s_endk == SIZEZ-1)
   {
     for(i = s_starti-1; i < s_endi+1; i++)
      for(j = s_startj-1; j < s_endj+1; j++)
          athread_get(PE_MODE,&A[src][i][j][s_startk-1]
                   , &A_slave_data[i-s_starti+1][j-s_startj+1][0]
                   , (s_endk-s_startk+2)*8,&get_reply,0,0,0);
   }else
   {
     for(i = s_starti-1; i < s_endi+1; i++)
     {
       int tsize, bsize, stride;
       bsize=(s_endk-s_startk+2)*8;
       stride=(SIZEZ-(s_endk-s_startk+2))*8;  //stride: from blocktail to blockhead 
       tsize= bsize*(s_endj-s_startj+2);
       athread_get(PE_MODE,&A[src][i][s_startj-1][s_startk-1]
                 , &A_slave_data[i-s_starti+1][0][0]
                 , tsize, &get_reply
                 , 0, stride, bsize);
     }
   }
#elif SPLIT_3D
    for(i = s_starti-1; i < s_endi+1; i++)
     for(j = s_startj-1; j < s_endj+1; j++)
          athread_get(PE_MODE,&A[src][i][j][s_startk-1]
                   , &A_slave_data[i-s_starti+1][j-s_startj+1][0]
                   , (s_endk-s_startk+2)*8,&get_reply,0,0,0);
#else
     for(i = s_starti-1; i < s_endi+1; i++) 
     athread_get(PE_MODE,&A[src][i][s_startj-1][s_startk-1]
                 , &A_slave_data[i-s_starti+1][0][0]
                 , (s_endj-s_startj+2)*(s_endk-s_startk+2)*8,&get_reply,0,0,0);
#endif
//1.2 synchronize, wait for the transfer finish
#ifdef __STRIDE_DMA
   if((SIZEZ-2)%BLKZ != 0 && s_endk == SIZEZ-1)
   {
     while(get_reply!=(s_endi-s_starti+2)*(s_endj-s_startj+2));
   }else
     while(get_reply!=(s_endi-s_starti+2));
#elif SPLIT_3D
  while(get_reply!=(s_endi-s_starti+2)*(s_endj-s_startj+2));
#else 
  while(get_reply!=(s_endi-s_starti+2));
#endif
//2. computation
  for(i = 1; i < s_endi-s_starti+1; i++)
    for(j = 1; j < s_endj-s_startj+1; j++)
      for(k = 1; k < s_endk-s_startk+1; k++) 
         A_slave_comp[i-1][j-1][k-1] = alpha_d * (A_slave_data[i][j][k]) + \
                     beta_d * (A_slave_data[i-1][j][k] + A_slave_data[i+1][j][k] +\
                     A_slave_data[i][j-1][k] + A_slave_data[i][j+1][k] +\
                     A_slave_data[i][j][k-1] + A_slave_data[i][j][k+1]);   
//3.1 write back
  put_reply=0;
#ifdef __STRIDE_DMA
 if((SIZEZ-2)%BLKZ != 0 && s_endk == SIZEZ-1)
 {
   for(i =  s_starti; i < s_endi; ++i)
      for(j = s_startj; j < s_endj; j++) 
        athread_put(PE_MODE,&A_slave_comp[i-s_starti][j-s_startj][0]
                     , &A[dest][i][j][s_startk]
                     ,(s_endk-s_startk)*8,&put_reply,0,0);
   }else 
   {	
     for(i =  s_starti; i < s_endi; ++i)
     {
       int tsize, bsize, stride;
       bsize=(s_endk-s_startk)*8;
       stride=(SIZEZ-(s_endk-s_startk))*8;  //stride: from blocktail to blockhead
       tsize= bsize*(s_endj-s_startj);
       athread_put(PE_MODE,&A_slave_comp[i-s_starti][0][0]
                , &A[dest][i][s_startj][s_startk]
                ,tsize,&put_reply,stride,bsize);
     }
   }
#else
  for(i =  s_starti; i < s_endi; ++i)
   for(j =  s_startj; j < s_endj; ++j)  
       athread_put(PE_MODE,&A_slave_comp[i-s_starti][j-s_startj][0]
                     , &A[dest][i][j][s_startk]
                     ,(s_endk-s_startk)*8,&put_reply,0,0);
#endif
//3.2 synchronize, wait for writing back finish.
#ifdef __STRIDE_DMA
  if((SIZEZ-2)%BLKZ != 0 && s_endk == SIZEZ-1)
  {
    while(put_reply!=(s_endi-s_starti)*(s_endj-s_startj));
  }else
   while(put_reply!=(s_endi-s_starti));  
#else
  while(put_reply!=(s_endi-s_starti)*(s_endj-s_startj)); 
#endif
}
